package com.lx.common.utils;

import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 通用类型工具类
 *
 * @author lx
 * @create 2023/1/11 17:04
 **/
public class CommonClassUtil {

    /**
     * 日志输出
     */
    protected static final Log log = LogFactory.get(CommonClassUtil.class);

    /**
     * 根据包装类类型将待转换值变更为原始(包装类)类型
     *
     * @param primitiveClass
     * @param convertValue
     * @return
     */
    public static Object convertToPrimitiveValue(Class<?> primitiveClass, Object convertValue) {
        if (convertValue == null || primitiveClass == null) {
            return null;
        }
        Class<?> aClass = convertValue.getClass();
        if (aClass.isPrimitive() && primitiveClass.equals(aClass)) {
            return convertValue;
        }
        try {
            String vv = String.valueOf(convertValue);

            if (Boolean.class.equals(primitiveClass)) {
                return Boolean.parseBoolean(vv);
            }
            if (Integer.class.equals(primitiveClass)) {
                return Integer.parseInt(vv);
            }
            if (Byte.class.equals(primitiveClass)) {
                return Byte.parseByte(vv);
            }
            if (Short.class.equals(primitiveClass)) {
                return Short.parseShort(vv);
            }
            if (Long.class.equals(primitiveClass)) {
                return Long.parseLong(vv);
            }
            if (Float.class.equals(primitiveClass)) {
                return Float.parseFloat(vv);
            }
            if (Double.class.equals(primitiveClass)) {
                return Double.parseDouble(vv);
            }
            if (Character.class.equals(primitiveClass)) {
                return vv.charAt(0);
            }
        } catch (Exception e) {
            log.error("转换结果=[{}]为原始类型失败,期望转换的结果类型为=[{}],失败原因:{}", new Object[]{convertValue, primitiveClass, e});
        }
        return null;
    }


    /**
     * 从类中寻找符合所有注解形式的属性对象并返回第一个属性
     *
     * @param clazz
     * @param annoClassArray
     * @return
     */
    public static Field findAnnotationFieldFromClass(Class<?> clazz, Class<? extends Annotation>... annoClassArray) {
        if (clazz == null) {
            return null;
        }
        Field[] clazzFields = ReflectUtil.getFields(clazz);

        boolean anoIsEmpty = ArrayUtil.isEmpty(annoClassArray);
        for (Field field : clazzFields) {
            if (anoIsEmpty) {
                continue;
            }
            boolean canWrite = true;
            for (Class<? extends Annotation> annoClass : annoClassArray) {
                if (ArrayUtil.isEmpty(field.getAnnotationsByType(annoClass))) {
                    canWrite = false;
                    break;
                }
            }
            if (canWrite) {
                return field;
            }
        }
        return null;
    }

    /**
     * 从类中寻找符合所有注解形式的属性对象并返回所有符合条件的属性列表
     *
     * @param clazz
     * @param anoClassArray
     * @return
     */
    public static List<Field> findAnnotationFieldListFromClass(Class<?> clazz, Class<? extends Annotation>... anoClassArray) {
        List<Field> fields = new ArrayList<>(8);
        if (clazz == null) {
            return fields;
        }
        Field[] clazzFields = ReflectUtil.getFields(clazz);

        boolean anoIsEmpty = ArrayUtil.isEmpty(anoClassArray);
        for (Field field : clazzFields) {
            if (anoIsEmpty) {
                continue;
            }
            boolean canWrite = true;
            for (Class<? extends Annotation> annoClass : anoClassArray) {
                if (ArrayUtil.isEmpty(field.getAnnotationsByType(annoClass))) {
                    canWrite = false;
                    break;
                }
            }
            if (canWrite) {
                fields.add(field);
            }
        }
        return fields;
    }


    /**
     * 尝试根据Class类型的无参构造方法创建一个对象实例
     * 当Class为下列接口类型时,提供默认转换实例
     * 1.Map => HashMap
     * 2.List => ArrayList
     * 3.Set => HashSet
     *
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> T richNewInstance(Class<T> tClass) {
        if (tClass == null) {
            return null;
        }
        if (!tClass.isInterface()) {
            return ReflectUtil.newInstance(tClass);
        }

        Object newInstance;
        if (Map.class.equals(tClass)) {
            newInstance = new HashMap<>();
        } else if (List.class.equals(tClass)) {
            newInstance = new ArrayList<>();
        } else if (Set.class.equals(tClass)) {
            newInstance = new HashSet<>();
        } else {
            //其他接口转换暂不支持.
            throw new UtilException("Instance class [{}] error!", tClass);
        }
        return (T) newInstance;
    }

    /**
     * 根据属性名称和java类型，获取对应的getter方法类
     *
     * @param propertyName
     * @param javaType
     * @return
     */
    public static Method getGetterMethod(Class<?> javaType, String propertyName) {
        if (javaType == null || StrUtil.isEmpty(propertyName)) {
            return null;
        }
        Field field = ReflectUtil.getField(javaType, propertyName);
        return getGetterMethod(javaType, field);
    }

    /**
     * 通过属性类型和java类型，获取对应的getter方法类
     *
     * @param javaType
     * @param field
     * @return
     */
    public static Method getGetterMethod(Class<?> javaType, Field field) {
        if (javaType == null || field == null) {
            return null;
        }
        //尝试使用get进行解析
        String propertyName = field.getName(), prefix = "get";
        Method res = ReflectUtil.getMethodByName(javaType, prefix + StrUtil.upperFirst(propertyName));

        if (res == null && "boolean".equals(field.getType().getTypeName())) {
            prefix = "is";
            res = ReflectUtil.getPublicMethod(javaType, prefix + StrUtil.upperFirst(propertyName));
        }
        return res;
    }

}
